package com.oauthorization.server.entity;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;

import java.io.Serializable;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class BaseResult<T> implements Serializable {

    private Integer code;
    private String msg;
    private T data;

    private BaseResult() {
    }

    /**
     * 断言返回数据是否成功，失败时抛出RuntimeException异常
     */
    public static void assertIsSuccess(BaseResult<?> result) {
        assertIsSuccess(result, RuntimeException::new, "返回数据错误");
    }

    /**
     * 断言返回数据是否成功，失败时抛出异常
     *
     * @param result    返回数据
     * @param function  返回异常方法
     * @param prefixMsg 异常提示前缀
     * @param <X>       异常类型
     * @throws X
     */
    public static <X extends Throwable> void assertIsSuccess(BaseResult<?> result, Function<String, X> function, String prefixMsg) throws X {
        if (Objects.isNull(result)) {
            throw function.apply(prefixMsg + " result is null");
        } else if (!result.isSuccess()) {
            throw function.apply(Objects.nonNull(result.getMsg()) ? prefixMsg + result.getMsg() : prefixMsg);
        }
    }

    public boolean isSuccess() {
        return Objects.nonNull(code) && code.equals(ResultCodeEnum.RESULT_CODE_SUCCESS.getCode());
    }

    /**
     * 返回data时，如果data为空抛出异常
     *
     * @param exceptionSupplier 异常供应者
     * @param <X>               异常类型
     * @return
     * @throws X
     */
    public <X extends Throwable> T getDataThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (Objects.isNull(this.data)) {
            throw exceptionSupplier.get();
        }
        return this.data;
    }

    /**
     * @param bool
     * @return true:操作成功 false:操作失败
     */
    public static BaseResult<Boolean> result(Boolean bool) {
        BaseResult<Boolean> result = new BaseResult<>();
        ResultCodeEnum resultCodeEnum = bool ? ResultCodeEnum.RESULT_CODE_SUCCESS : ResultCodeEnum.RESULT_CODE_FAIL;
        result.setCode(resultCodeEnum.getCode());
        result.setMsg(resultCodeEnum.getMessage());
        result.setData(bool);
        return result;
    }

    //成功
    public static BaseResult success() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_SUCCESS.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_SUCCESS.getMessage());
        return result;
    }

    public static <T> BaseResult<T> success(T data) {
        BaseResult<T> result = new BaseResult<>();
        result.setCode(ResultCodeEnum.RESULT_CODE_SUCCESS.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_SUCCESS.getMessage());
        result.setData(data);
        return result;
    }

    public static <T> BaseResult<T> success(String msg, T data) {
        BaseResult<T> result = new BaseResult<>();
        result.setCode(ResultCodeEnum.RESULT_CODE_SUCCESS.getCode());
        result.setMsg(msg);
        result.setData(data);
        return result;
    }

    public static BaseResult failure() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_FAIL.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_FAIL.getMessage());
        return result;
    }

    public static BaseResult failure(String msg) {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_FAIL.getCode());
        result.setMsg(msg);
        return result;
    }

    public static BaseResult unauthorized() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_UNAUTHORIZED.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_UNAUTHORIZED.getMessage());
        return result;
    }

    public static BaseResult forbidden() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_FORBIDDEN.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_FORBIDDEN.getMessage());
        return result;
    }

    public static BaseResult accessDenied() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_FAIL.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_FORBIDDEN.getMessage());
        return result;
    }


    public static BaseResult notFound() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_NOT_FOUND.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_NOT_FOUND.getMessage());
        return result;
    }

    public static BaseResult methodNotAllowed() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_METHOD_NOT_ALLOWED.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_METHOD_NOT_ALLOWED.getMessage());
        return result;
    }

    public static BaseResult unsupportedMediaType() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_UNSUPPORTED_MEDIA_TYPE.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_UNSUPPORTED_MEDIA_TYPE.getMessage());
        return result;
    }

    public static BaseResult trafficLimiting() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_TRAFFIC_LIMITING.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_TRAFFIC_LIMITING.getMessage());
        return result;
    }

    public static BaseResult paramError() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_PARAM_ERROR.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_PARAM_ERROR.getMessage());
        return result;
    }

    public static BaseResult paramError(String message) {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_PARAM_ERROR.getCode());
        result.setMsg(message);
        return result;
    }

    public static BaseResult ajaxSessionTimeout() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_AJAX_SESSION_TIME_OUT.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_AJAX_SESSION_TIME_OUT.getMessage());
        return result;
    }

    public static BaseResult serverError() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_SERVER_ERROR.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_SERVER_ERROR.getMessage());
        return result;
    }

    public static BaseResult rpcError() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_RPC_ERROR.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_RPC_ERROR.getMessage());
        return result;
    }

    public static BaseResult businessError() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_BUSINESS_ERROR.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_BUSINESS_ERROR.getMessage());
        return result;
    }

    public static BaseResult insertDataError() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_INSERT_DATA_ERROR.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_INSERT_DATA_ERROR.getMessage());
        return result;
    }

    public static BaseResult deleteDataError() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_DELETE_DATA_ERROR.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_DELETE_DATA_ERROR.getMessage());
        return result;
    }

    public static BaseResult updateDataError() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_UPDATE_DATA_ERROR.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_UPDATE_DATA_ERROR.getMessage());
        return result;
    }

    public static BaseResult selectDataError() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_SELECT_DATA_ERROR.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_SELECT_DATA_ERROR.getMessage());
        return result;
    }

    public static BaseResult dataNotFoundError() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_DATA_NOT_FOUND_ERROR.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_DATA_NOT_FOUND_ERROR.getMessage());
        return result;
    }

    public static BaseResult dataExistError() {
        BaseResult result = new BaseResult();
        result.setCode(ResultCodeEnum.RESULT_CODE_DATA_EXIST_ERROR.getCode());
        result.setMsg(ResultCodeEnum.RESULT_CODE_DATA_EXIST_ERROR.getMessage());
        return result;
    }

    public enum ResultCodeEnum {
        RESULT_CODE_SUCCESS(0, "操作成功~"),//成功
        RESULT_CODE_FAIL(1, "操作失败~"),//失败
        RESULT_CODE_UNAUTHORIZED(401, "用户未认证~"),
        RESULT_CODE_FORBIDDEN(403, "对不起，您没有访问权限~"),
        RESULT_CODE_NOT_FOUND(404, String.format("哎呀，无法找到这个资源啦(%s)", "Not Found")),
        RESULT_CODE_METHOD_NOT_ALLOWED(405, String.format("请换个姿势操作试试(%s)", "Method Not Allowed")),
        RESULT_CODE_UNSUPPORTED_MEDIA_TYPE(415, String.format("呀，不支持该媒体类型(%s)", "Unsupported Media Type")),
        RESULT_CODE_TRAFFIC_LIMITING(429, "哎呀，网络拥挤请稍后再试试"),
        RESULT_CODE_PARAM_ERROR(488, "参数错误"),
        RESULT_CODE_AJAX_SESSION_TIME_OUT(499, "Ajax访问Session超时~"),
        RESULT_CODE_SERVER_ERROR(500, "服务器开小差，请稍后再试~"),
        RESULT_CODE_RPC_ERROR(510, "呀，网络出问题啦！"),
        RESULT_CODE_BUSINESS_ERROR(9999, "业务异常"),
        RESULT_CODE_INSERT_DATA_ERROR(9001, "添加数据失败"),
        RESULT_CODE_DELETE_DATA_ERROR(9002, "删除数据失败"),
        RESULT_CODE_UPDATE_DATA_ERROR(9003, "修改数据失败"),
        RESULT_CODE_SELECT_DATA_ERROR(9004, "查询数据失败"),
        RESULT_CODE_DATA_NOT_FOUND_ERROR(9005, "数据不存在"),
        RESULT_CODE_DATA_EXIST_ERROR(9006, "数据已存在"),
        RESULT_SUPPLIER_DATA_EXIST_ERROR(9007, "供应商不存在");

        public int code;
        public String message;

        ResultCodeEnum(int code, String message) {
            this.code = code;
            this.message = message;
        }

        public int getCode() {
            return this.code;
        }

        public String getMessage() {
            return this.message;
        }
    }
}